#include	"system.h"

#if defined(_WIN32)
#	include		<windows.h>
#	include		<direct.h>
#	include		<io.h>
#else
#	include		<limits.h>
#	include		<dirent.h>
#	include		<unistd.h>
#	include		<sys/stat.h>
#endif

#include	<cstring>
#include	<vector>

using namespace std;

#if defined(_WIN32)
uint64_t Tick() {
	static bool		bInited = false;
	static double	dFreq	= 0;

	if (!bInited) {
		LARGE_INTEGER iPerfFreq;
		if (QueryPerformanceFrequency(&iPerfFreq)) {
			dFreq = 1.0 / iPerfFreq.QuadPart;
		} else {
			dFreq = 0;
		}

		bInited = true;
	}

	LARGE_INTEGER iCounter;
	if (!QueryPerformanceCounter(&iCounter)) return 0;
	return (uint64_t)((double)iCounter.QuadPart * dFreq * 1000);
}
#else
uint64_t Tick() {
	struct timespec iClock;
	clock_gettime(CLOCK_MONOTONIC, &iClock);
	return (uint64_t)iClock.tv_sec * 1000 + iClock.tv_nsec / 1000000;
}
#endif

DateTime::DateTime() {	
#if defined(_WIN32)
	SYSTEMTIME iInfo;
	GetLocalTime(&iInfo);

	nYear		= iInfo.wYear;
	nMonth		= iInfo.wMonth;
	nDay	 	= iInfo.wDay;
	nWeek		= iInfo.wDayOfWeek;
	nHour		= iInfo.wHour;
	nMin		= iInfo.wMinute;
	nSec		= iInfo.wSecond;
	nMSec		= iInfo.wMilliseconds;
#else
	struct tm iInfo;
	struct timespec iDetail;

	clock_gettime(CLOCK_REALTIME, &iDetail);
	localtime_r(&iDetail.tv_sec, &iInfo);

	nYear		= iInfo.tm_year + 1900;
	nMonth		= iInfo.tm_mon + 1;
	nDay	 	= iInfo.tm_mday;
	nWeek		= iInfo.tm_wday;
	nHour		= iInfo.tm_hour;
	nMin		= iInfo.tm_min;
	nSec		= iInfo.tm_sec;
	nMSec		= iDetail.tv_nsec / 1000000;
#endif
}

DateTime::DateTime(const DateTime & r)
	: nYear(r.nYear)
	, nMonth(r.nMonth)
	, nDay(r.nDay)
	, nWeek(r.nWeek)
	, nHour(r.nHour)
	, nMin(r.nMin)
	, nSec(r.nSec)
	, nMSec(r.nMSec) {}

DateTime::DateTime(int nY, int nM, int nD, int nH, int nMin, int nS)
	: nYear(nY)
	, nMonth(nM)
	, nDay(nD)
	, nWeek(0)
	, nHour(nH)
	, nMin(nMin)
	, nSec(nS)
	, nMSec(0) {
	struct tm	iTime;

	iTime.tm_year	= nY - 1900;
	iTime.tm_mon	= nM - 1;
	iTime.tm_mday	= nD;
	iTime.tm_hour	= nH;
	iTime.tm_min	= nMin;
	iTime.tm_sec	= nS;

	mktime(&iTime);
	nWeek = iTime.tm_wday;
}

DateTime::DateTime(uint64_t nHRTime) {
	time_t nTime = (time_t)(nHRTime / 1000);
	struct tm iTime;

#if defined(_WIN32)
	localtime_s(&iTime, &nTime);
#else
	localtime_r(&nTime, &iTime);
#endif

	nYear		= iTime.tm_year + 1900;
	nMonth		= iTime.tm_mon + 1;
	nDay		= iTime.tm_mday + 1;
	nWeek		= iTime.tm_wday;
	nHour		= iTime.tm_hour;
	nMin		= iTime.tm_min;
	nSec		= iTime.tm_sec;
	nMSec		= nTime % 1000;
}

uint64_t DateTime::Time() {
	struct tm	iTime;

	iTime.tm_year	= nYear - 1900;
	iTime.tm_mon	= nMonth - 1;
	iTime.tm_mday	= nDay;
	iTime.tm_hour	= nHour;
	iTime.tm_min	= nMin;
	iTime.tm_sec	= nSec;

	return mktime(&iTime) * 1000 + nMSec;
}

bool Path::Exists(const string & sPath) {
#if defined(_WIN32)
	return _access(sPath.c_str(), 0) == 0;
#else
	return access(sPath.c_str(), 0) == 0;
#endif
}

bool Path::Create(const string & sPath) {
#if defined(_WIN32)
	return _mkdir(sPath.c_str()) == 0;
#else
	return mkdir(sPath.c_str(), 755) == 0;
#endif
}

string Path::Current() {
	std::string sPath(512, '0');
#if defined(_WIN32)
	_getcwd((char *)sPath.c_str(), 512);
#else
	char * pUnused = getcwd((char *)sPath.c_str(), 512);
	(void)pUnused;
#endif
	return std::move(sPath);
}

bool Path::Change(const string & sPath) {
#if defined(_WIN32)
	return (0 == _chdir(sPath.c_str()));
#else
	return (0 == chdir(sPath.c_str()));
#endif
}

string Path::FullPath(const string & sPath) {
	string s(512, '0');
#if defined(_WIN32)
	if (0 >= GetFullPathNameA(sPath.c_str(), 512, (char *)s.c_str(), NULL)) return sPath;
#else
	if (!realpath(sPath.c_str(), (char *)s.c_str())) return sPath;
#endif
	return move(s);
}

string Path::PurePath(const string & sPath) {
	if (sPath.empty() || sPath.length() <= 0) return "";
	for (size_t nIdx = sPath.length() - 1; nIdx >= 0; --nIdx) {
		if (sPath[nIdx] == '/' || sPath[nIdx] == '\\') {
			if (nIdx != 0) return sPath.substr(0, nIdx);
		}
	}
	return "./";
}

string Path::PureFile(const string & sPath) {
	if (sPath.empty() || sPath.length() <= 0) return "";
	for (size_t nIdx = sPath.length() - 1; nIdx >= 0; --nIdx) {
		if (sPath[nIdx] == '/' || sPath[nIdx] == '\\') {
			return sPath.substr(nIdx + 1);
		}
	}
	return sPath;
}

void Path::Traverse(const string & sPath, function<void (const string &)> fOpt, bool bRecursive) {
	vector<string> vSubDir;

#if defined(_WIN32)
	WIN32_FIND_DATAA iFind;
	HANDLE hResult;

	std::string sFinder(sPath);
	sFinder.append("\\*.*");

	hResult = ::FindFirstFileA(sFinder.c_str(), &iFind);
	if (hResult == INVALID_HANDLE_VALUE)
		return;
	
	while (::FindNextFileA(hResult, &iFind)) {
		std::string sData(sPath);
		sData.append("\\");
		sData.append(iFind.cFileName);
		if (iFind.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
			if (!bRecursive)
				continue;
			else if (strcmp(iFind.cFileName, ".") == 0 || strcmp(iFind.cFileName, "..") == 0)
				continue;
			else
				vSubDir.push_back(sData);
		} else {
			fOpt(sData);
		}
	}

	::FindClose(hResult);
#else
	DIR *			pDir;
	struct dirent *	pFile;
	struct stat		iStat;

	if (!(pDir = ::opendir(sPath.c_str()))) return;

	while (NULL != (pFile = ::readdir(pDir))) {
		std::string sData(sPath);
		sData.append("/");
		sData.append(pFile->d_name);

		if (strncmp(pFile->d_name, ".", 1) == 0)
			continue;
		else if (::stat(sData.c_str(), &iStat) < 0)
			continue;			

		if (S_ISDIR(iStat.st_mode)) {
			if (bRecursive) vSubDir.push_back(sData);
		} else if (S_ISREG(iStat.st_mode)) {
			fOpt(sData);
		}
	}

	::closedir(pDir);
#endif

	for (size_t nIdx = 0; nIdx < vSubDir.size(); ++nIdx)
		Traverse(vSubDir[nIdx], fOpt);
}
